<meta charset="utf-8">
(#) Avoid using UAST implementation

!!! WARNING: Avoid using UAST implementation
   This is a warning.

Id
:   `UastImplementation`
Summary
:   Avoid using UAST implementation
Severity
:   Warning
Category
:   Lint Implementation Issues
Platform
:   JDK
Vendor
:   Android Open Source Project
Feedback
:   https://issuetracker.google.com/issues/new?component=192708
Since
:   7.2.0 (May 2022)
Affects
:   Kotlin and Java files
Editing
:   This check runs on the fly in the IDE editor
Implementation
:   [Source Code](https://cs.android.com/android-studio/platform/tools/base/+/mirror-goog-studio-main:lint/libs/lint-checks/src/main/java/com/android/tools/lint/checks/UastImplementationDetector.kt)
Tests
:   [Source Code](https://cs.android.com/android-studio/platform/tools/base/+/mirror-goog-studio-main:lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/UastImplementationDetectorTest.kt)

Use UAST interface whenever possible, and do not rely on UAST
implementation, which is subject to change. If language-specific
information is needed, the next option is to use PSI directly (though
these APIs are less stable and can depend on compiler internals,
especially in the case of Kotlin).

(##) Example

Here is an example of lint warnings produced by this check:
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~text
src/test/pkg/UastImplementationDetectorTestInput.kt:18:Warning:
org.jetbrains.uast.kotlin.KotlinUField is UAST implementation. Consider
using one of its corresponding UAST interfaces: UVariableEx, UVariable,
UDeclaration, UAnnotated, UDeclarationEx, UAnchorOwner, UFieldEx, UField
[UastImplementation]
import org.jetbrains.uast.kotlin.KotlinUField // ERROR 1
--------------------------------------------------------
src/test/pkg/UastImplementationDetectorTestInput.kt:19:Warning:
org.jetbrains.uast.kotlin.KotlinUImportStatement is UAST implementation.
Consider using one of its corresponding UAST interfaces:
UImportStatement, UResolvable [UastImplementation]
import org.jetbrains.uast.kotlin.KotlinUImportStatement // ERROR 2
------------------------------------------------------------------
src/test/pkg/UastImplementationDetectorTestInput.kt:20:Warning:
org.jetbrains.uast.kotlin.KotlinUThisExpression is UAST implementation.
Consider using one of its corresponding UAST interfaces: UExpression,
UAnnotated, UThisExpression, UInstanceExpression, ULabeled, UResolvable
[UastImplementation]
import org.jetbrains.uast.kotlin.KotlinUThisExpression // ERROR 3
-----------------------------------------------------------------
src/test/pkg/UastImplementationDetectorTestInput.kt:21:Warning:
org.jetbrains.uast.kotlin.KotlinUastResolveProviderService is UAST
implementation. Consider using one of its corresponding UAST interfaces:
BaseKotlinUastResolveProviderService [UastImplementation]
import org.jetbrains.uast.kotlin.KotlinUastResolveProviderService // ERROR 4
----------------------------------------------------------------------------
src/test/pkg/UastImplementationDetectorTestInput.kt:22:Warning:
org.jetbrains.uast.kotlin.UnknownKotlinExpression is UAST
implementation. Consider using one of its corresponding UAST interfaces:
UExpression, UAnnotated, UUnknownExpression [UastImplementation]
import org.jetbrains.uast.kotlin.UnknownKotlinExpression // ERROR 5
-------------------------------------------------------------------
src/test/pkg/UastImplementationDetectorTestInput.kt:45:Warning:
org.jetbrains.uast.kotlin.KotlinUField is UAST implementation. Consider
using one of its corresponding UAST interfaces: UVariableEx, UVariable,
UDeclaration, UAnnotated, UDeclarationEx, UAnchorOwner, UFieldEx, UField
[UastImplementation]
    val delegateType = (field as? KotlinUField)?.type // ERROR 6
                                  ------------
src/test/pkg/UastImplementationDetectorTestInput.kt:57:Warning:
org.jetbrains.uast.kotlin.KotlinUImportStatement is UAST implementation.
Consider using one of its corresponding UAST interfaces:
UImportStatement, UResolvable [UastImplementation]
    val alias = (node as? KotlinUImportStatement)?.sourcePsi?.alias // ERROR 7
                          ----------------------
src/test/pkg/UastImplementationDetectorTestInput.kt:63:Warning:
org.jetbrains.uast.kotlin.KotlinUThisExpression is UAST implementation.
Consider using one of its corresponding UAST interfaces: UExpression,
UAnnotated, UThisExpression, UInstanceExpression, ULabeled, UResolvable
[UastImplementation]
  class MockKtThisChecker : AbstractDetector(KotlinUThisExpression::class) { // ERROR 8
                                             ----------------------------
src/test/pkg/UastImplementationDetectorTestInput.kt:76:Warning:
org.jetbrains.uast.kotlin.KotlinUThisExpression is UAST implementation.
Consider using one of its corresponding UAST interfaces: UExpression,
UAnnotated, UThisExpression, UInstanceExpression, ULabeled, UResolvable
[UastImplementation]
    firstElement is KotlinUThisExpression && firstElement.label != null -&gt; { // ERROR 9
                    ---------------------
src/test/pkg/UastImplementationDetectorTestInput.kt:87:Warning:
org.jetbrains.uast.kotlin.KotlinUastResolveProviderService is UAST
implementation. Consider using one of its corresponding UAST interfaces:
BaseKotlinUastResolveProviderService [UastImplementation]
    ServiceManager.getService(this.project, KotlinUastResolveProviderService::class.java) // ERROR 10
                                            ---------------------------------------
src/test/pkg/UastImplementationDetectorTestInput.kt:93:Warning:
org.jetbrains.uast.kotlin.UnknownKotlinExpression is UAST
implementation. Consider using one of its corresponding UAST interfaces:
UExpression, UAnnotated, UUnknownExpression [UastImplementation]
    return if (this !is UnknownKotlinExpression) this else null // ERROR 11
                        -----------------------
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

Here is the source file referenced above:

`src/test/pkg/UastImplementationDetectorTestInput.kt`:
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~kotlin linenumbers
/* Copyright (C) 2021 The Android Open Source Project */
package test.pkg

import com.android.tools.lint.client.api.UElementHandler
import com.android.tools.lint.detector.api.Detector
import com.android.tools.lint.detector.api.JavaContext
import com.android.tools.lint.detector.api.Scope
import com.android.tools.lint.detector.api.Severity
import com.intellij.openapi.components.ServiceManager
import org.jetbrains.kotlin.psi.KtElement
import org.jetbrains.kotlin.resolve.BindingContext
import org.jetbrains.uast.UClass
import org.jetbrains.uast.UElement
import org.jetbrains.uast.UExpression
import org.jetbrains.uast.UField
import org.jetbrains.uast.UImportStatement
import org.jetbrains.uast.UThisExpression
import org.jetbrains.uast.kotlin.KotlinUField // ERROR 1
import org.jetbrains.uast.kotlin.KotlinUImportStatement // ERROR 2
import org.jetbrains.uast.kotlin.KotlinUThisExpression // ERROR 3
import org.jetbrains.uast.kotlin.KotlinUastResolveProviderService // ERROR 4
import org.jetbrains.uast.kotlin.UnknownKotlinExpression // ERROR 5

class UastImplementationDetectorTestInput {

  // Partial definition from Google internal
  abstract class AbstractDetector(private val uastNodes: Iterable<KClass<out UElement>>) :
    Detector() {

      constructor(
        uastNode: KClass<out UElement>,
        vararg moreUastNodes: KClass<out UElement>
      ) : this(listOf(uastNode, *moreUastNodes))
  }

  class MockSafetyDetector : AbstractDetector(UClass::class) {
    override fun createUastHandler(context: JavaContext): UElementHandler? {
      return object : UElementHandler() {
        override fun visitClass(node: UClass) {
          node.fields.forEach { field -> checkFieldSafety(field) }
        }

        private fun checkFieldSafety(field: UField) {
          if (field.name.endsWith("delegate")) {
            val delegateType = (field as? KotlinUField)?.type // ERROR 6
            assert (delegateType != null)
          }
        }
      }
    }
  }

  class MockCoroutineChecker : AbstractDetector(UImportStatement::class) {
    override fun createUastHandler(context: JavaContext): UElementHandler? {
      return object : UElementHandler() {
        override fun visitImportStatement(node: UImportStatement) {
          val alias = (node as? KotlinUImportStatement)?.sourcePsi?.alias // ERROR 7
        }
      }
    }
  }

  class MockKtThisChecker : AbstractDetector(KotlinUThisExpression::class) { // ERROR 8
    override fun createUastHandler(context: JavaContext): UElementHandler? {
      return object : UElementHandler() {
        override fun visitElement(node: UElement) {
        }
      }
    }
  }

  private fun trimTrivialThisExpr(expr: UExpression): UExpression? {
    var firstElement = expr

    return when {
      firstElement is KotlinUThisExpression && firstElement.label != null -> { // ERROR 9
        expr
      }
      firstElement is UThisExpression -> null
      else -> expr
    }
  }

  fun KtElement.getBindingContext(): BindingContext {
    // OK for now to retrieve a service. Won't be allowed after switching to K2 UAST
    val service =
      ServiceManager.getService(this.project, KotlinUastResolveProviderService::class.java) // ERROR 10
    return service?.getBindingContext(this) ?: BindingContext.EMPTY
  }

  internal val UElement.realUastParent: UElement?
    get() {
      return if (this !is UnknownKotlinExpression) this else null // ERROR 11
    }
}
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

You can also visit the
[source code](https://cs.android.com/android-studio/platform/tools/base/+/mirror-goog-studio-main:lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/UastImplementationDetectorTest.kt)
for the unit tests for this check to see additional scenarios.

(##) Suppressing

You can suppress false positives using one of the following mechanisms:

* Using a suppression annotation like this on the enclosing
  element:

  ```kt
  // Kotlin
  @Suppress("UastImplementation")
  fun method() {
     problematicStatement()
  }
  ```

  or

  ```java
  // Java
  @SuppressWarnings("UastImplementation")
  void method() {
     problematicStatement();
  }
  ```

* Using a suppression comment like this on the line above:

  ```kt
  //noinspection UastImplementation
  problematicStatement()
  ```

* Using a special `lint.xml` file in the source tree which turns off
  the check in that folder and any sub folder. A simple file might look
  like this:
  ```xml
  &lt;?xml version="1.0" encoding="UTF-8"?&gt;
  &lt;lint&gt;
      &lt;issue id="UastImplementation" severity="ignore" /&gt;
  &lt;/lint&gt;
  ```
  Instead of `ignore` you can also change the severity here, for
  example from `error` to `warning`. You can find additional
  documentation on how to filter issues by path, regular expression and
  so on
  [here](https://googlesamples.github.io/android-custom-lint-rules/usage/lintxml.md.html).

* In Gradle projects, using the DSL syntax to configure lint. For
  example, you can use something like
  ```gradle
  lintOptions {
      disable 'UastImplementation'
  }
  ```
  In Android projects this should be nested inside an `android { }`
  block.

* For manual invocations of `lint`, using the `--ignore` flag:
  ```
  $ lint --ignore UastImplementation ...`
  ```

* Last, but not least, using baselines, as discussed
  [here](https://googlesamples.github.io/android-custom-lint-rules/usage/baselines.md.html).

<!-- Markdeep: --><style class="fallback">body{visibility:hidden;white-space:pre;font-family:monospace}</style><script src="markdeep.min.js" charset="utf-8"></script><script src="https://morgan3d.github.io/markdeep/latest/markdeep.min.js" charset="utf-8"></script><script>window.alreadyProcessedMarkdeep||(document.body.style.visibility="visible")</script>